
// WndBinder.cpp: implementation of the CWndBinder class.
//
//////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "MonitorClient.h"
#include "WndBinder.h"
#include "Utility.h"

#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

CWndBinder::CWndBinder()
: m_pParentWnd(NULL)
{

}

CWndBinder::~CWndBinder()
{

}

void CWndBinder::Reset()
{
	m_pParentWnd = NULL;
	m_WndBindMap.clear();
}

void CWndBinder::SetParent(CWnd* pWnd)
{
	Reset();

	m_pParentWnd = pWnd;
	pWnd->GetClientRect(&m_rectParentOrigin);
	m_rectParent = m_rectParentOrigin;
}

void CWndBinder::InitBindInfo(WND_BIND_INFO* pBindInfo, HWND hWnd)
{
	for(int i= 0; i < 4; ++i)
	{
		pBindInfo->m_BorderBindInfo[i].m_pBindData = NULL;
		pBindInfo->m_BorderBindInfo[i].m_nOffset = 0;
		pBindInfo->m_BorderBindInfo[i].Target = NOT_BIND;
	}

	pBindInfo->hWnd = hWnd;

	GetWindowRect(hWnd, &pBindInfo->m_rectWndOrigin);
	m_pParentWnd->ScreenToClient(&pBindInfo->m_rectWndOrigin);

	pBindInfo->m_rectWnd = pBindInfo->m_rectWndOrigin;

}

WND_BIND_INFO* CWndBinder::GetBindInfo(HWND hWnd)
{
	WND_BIND_INFO* pBindInfo = FindBindInfo(hWnd);

	if(pBindInfo == NULL)
	{
		pBindInfo = AddBindInfo(hWnd);
	}

	return pBindInfo;
}

WND_BIND_INFO* CWndBinder::AddBindInfo(HWND hWnd)
{
	WND_BIND_INFO BindInfo;

	InitBindInfo(&BindInfo, hWnd);
	
	pair<CWndBindMap::iterator, bool> r = m_WndBindMap.insert(CWndBindMap::value_type(hWnd, BindInfo));

	if(r.second)
	{
		return &(r.first->second);
	}
	else
	{
		return NULL;
	}
	
}

WND_BIND_INFO* CWndBinder::FindBindInfo(HWND hWnd)
{
	CWndBindMap::iterator it = m_WndBindMap.find(hWnd);

	if(it == m_WndBindMap.end())
	{
		return NULL;
	}
	else
	{
		return &(it->second);
	}
}

void CWndBinder::BindChildToParent(int nChildID, DWORD dwBindBorder, bool bBindToBorder)
{
	BindChildToParent(::GetDlgItem(m_pParentWnd->m_hWnd, nChildID), dwBindBorder, bBindToBorder);
}

void CWndBinder::BindChildToParent(CWnd* pChild, DWORD dwBindBorder, bool bBindToBorder)
{
	BindChildToParent(pChild->m_hWnd, dwBindBorder, bBindToBorder);
}

void CWndBinder::BindChildToParent(HWND hChild, DWORD dwBindBorder, bool bBindToBorder)
{
	if(bBindToBorder)
	{
		if(dwBindBorder & BIND_LEFT)
			BindToParentBorder(hChild, LEFT_BORDER, LEFT_BORDER);
		if(dwBindBorder & BIND_TOP)
			BindToParentBorder(hChild, TOP_BORDER, TOP_BORDER);
		if(dwBindBorder & BIND_RIGHT)
			BindToParentBorder(hChild, RIGHT_BORDER, RIGHT_BORDER);
		if(dwBindBorder & BIND_BOTTOM)
			BindToParentBorder(hChild, BOTTOM_BORDER, BOTTOM_BORDER);
	}
	else
	{
		WND_BIND_INFO* pBindInfo = GetBindInfo(hChild);

		if(dwBindBorder & BIND_LEFT)
			pBindInfo->m_BorderBindInfo[LEFT_BORDER].Target = PARENT_WND;
		if(dwBindBorder & BIND_TOP)
			pBindInfo->m_BorderBindInfo[TOP_BORDER].Target = PARENT_WND;
		if(dwBindBorder & BIND_RIGHT)
			pBindInfo->m_BorderBindInfo[RIGHT_BORDER].Target = PARENT_WND;
		if(dwBindBorder & BIND_BOTTOM)
			pBindInfo->m_BorderBindInfo[BOTTOM_BORDER].Target = PARENT_WND;
	}
}

void CWndBinder::BindToParentBorder(int nChildID, WND_BORDER BindBorder, WND_BORDER TargetBorder)
{
	BindToParentBorder(GetDlgItem(m_pParentWnd->m_hWnd, nChildID), BindBorder, TargetBorder);
}

void CWndBinder::BindToParentBorder(CWnd* pChild, WND_BORDER BindBorder, WND_BORDER TargetBorder)
{
	BindToParentBorder(pChild->m_hWnd, BindBorder, TargetBorder);
}

void CWndBinder::BindToParentBorder(HWND hChild, WND_BORDER BindBorder, WND_BORDER TargetBorder)
{
	WND_BIND_INFO* pBindInfo = GetBindInfo(hChild);

	BORDER_BIND_INFO& BorderBindInfo = pBindInfo->m_BorderBindInfo[BindBorder];
	int nBindBorderData = 0;
	switch(BindBorder)
	{
	case LEFT_BORDER:
		nBindBorderData = pBindInfo->m_rectWndOrigin.left;
		break;
	case TOP_BORDER:
		nBindBorderData = pBindInfo->m_rectWndOrigin.top;
		break;
	case RIGHT_BORDER:
		nBindBorderData = pBindInfo->m_rectWndOrigin.right;
		break;
	case BOTTOM_BORDER:
		nBindBorderData = pBindInfo->m_rectWndOrigin.bottom;
		break;
	}

	int nTargetBorderData = 0;
	switch(TargetBorder)
	{
	case LEFT_BORDER:
		nTargetBorderData = m_rectParentOrigin.left;
		BorderBindInfo.m_pBindData = &m_rectParent.left;
		break;
	case TOP_BORDER:
		nTargetBorderData = m_rectParentOrigin.top;
		BorderBindInfo.m_pBindData = &m_rectParent.top;
		break;
	case RIGHT_BORDER:
		nTargetBorderData = m_rectParentOrigin.right;
		BorderBindInfo.m_pBindData = &m_rectParent.right;
		break;
	case BOTTOM_BORDER:
		nTargetBorderData = m_rectParentOrigin.bottom;
		BorderBindInfo.m_pBindData = &m_rectParent.bottom;
		break;
	}

	BorderBindInfo.m_nOffset = nBindBorderData - nTargetBorderData;
	BorderBindInfo.Target = PARENT_WND_BORDER;

}

void CWndBinder::BindToParentBorder(int nChildID)
{
	BindToParentBorder(::GetDlgItem(m_pParentWnd->m_hWnd, nChildID));
}

void CWndBinder::BindToParentBorder(CWnd* pChild)
{
	BindToParentBorder(pChild->m_hWnd);
}

void CWndBinder::BindToParentBorder(HWND hChild)
{
	BindToParentBorder(hChild, LEFT_BORDER, LEFT_BORDER);
	BindToParentBorder(hChild, TOP_BORDER, TOP_BORDER);
	BindToParentBorder(hChild, RIGHT_BORDER, RIGHT_BORDER);
	BindToParentBorder(hChild, BOTTOM_BORDER, BOTTOM_BORDER);
}

void CWndBinder::BindChildToChild(int nChildID, WND_BORDER BindBorder, int nTargetID, WND_BORDER TargetBorder)
{
	HWND hChild = ::GetDlgItem(m_pParentWnd->m_hWnd, nChildID);
	HWND hTarget = ::GetDlgItem(m_pParentWnd->m_hWnd, nTargetID);
	
	BindChildToChild(hChild, BindBorder, hTarget, TargetBorder);
}

void CWndBinder::BindChildToChild(CWnd* pChild, WND_BORDER BindBorder, CWnd* pTarget, WND_BORDER TargetBorder)
{
	BindChildToChild(pChild->m_hWnd, BindBorder, pTarget->m_hWnd, TargetBorder);
}

void CWndBinder::BindChildToChild(HWND hChild, WND_BORDER BindBorder, HWND hTarget, WND_BORDER TargetBorder)
{
	WND_BIND_INFO* pTargetBindInfo = GetBindInfo(hTarget);
	WND_BIND_INFO* pBindInfo = GetBindInfo(hChild);

	BORDER_BIND_INFO& BorderBindInfo = pBindInfo->m_BorderBindInfo[BindBorder];
	int nBindBorderData = 0;
	switch(BindBorder)
	{
	case LEFT_BORDER:
		nBindBorderData = pBindInfo->m_rectWndOrigin.left;
		break;
	case TOP_BORDER:
		nBindBorderData = pBindInfo->m_rectWndOrigin.top;
		break;
	case RIGHT_BORDER:
		nBindBorderData = pBindInfo->m_rectWndOrigin.right;
		 break;
	case BOTTOM_BORDER:
		nBindBorderData = pBindInfo->m_rectWndOrigin.bottom;
		break;
	}

	int nTargetBorderData = 0;
	switch(TargetBorder)
	{
	case LEFT_BORDER:
		nTargetBorderData = pTargetBindInfo->m_rectWndOrigin.left;
		BorderBindInfo.m_pBindData = &(pTargetBindInfo->m_rectWnd.left);
		break;
	case TOP_BORDER:
		nTargetBorderData = pTargetBindInfo->m_rectWndOrigin.top;
		BorderBindInfo.m_pBindData = &(pTargetBindInfo->m_rectWnd.top);
		break;
	case RIGHT_BORDER:
		nTargetBorderData = pTargetBindInfo->m_rectWndOrigin.right;
		BorderBindInfo.m_pBindData = &(pTargetBindInfo->m_rectWnd.right);
		break;
	case BOTTOM_BORDER:
		nTargetBorderData = pTargetBindInfo->m_rectWndOrigin.bottom;
		BorderBindInfo.m_pBindData = &(pTargetBindInfo->m_rectWnd.bottom);
		break;
	}

	BorderBindInfo.m_nOffset = nBindBorderData - nTargetBorderData;
	BorderBindInfo.Target = SIBLING_WND;
}

void CWndBinder::CancelBind(int nChildID, DWORD dwBindBorder)
{
	CancelBind(GetDlgItem(m_pParentWnd->m_hWnd, nChildID), dwBindBorder);
}

void CWndBinder::CancelBind(CWnd* pChild, DWORD dwBindBorder)
{
	CancelBind(pChild->m_hWnd, dwBindBorder);
}

void CWndBinder::CancelBind(HWND hChild, DWORD dwBindBorder)
{
	WND_BIND_INFO* pBindInfo = GetBindInfo(hChild);

	if(dwBindBorder & BIND_LEFT)
		pBindInfo->m_BorderBindInfo[LEFT_BORDER].Target = NOT_BIND;
	if(dwBindBorder & BIND_TOP)
		pBindInfo->m_BorderBindInfo[TOP_BORDER].Target = NOT_BIND;
	if(dwBindBorder & BIND_RIGHT)
		pBindInfo->m_BorderBindInfo[RIGHT_BORDER].Target = NOT_BIND;
	if(dwBindBorder & BIND_BOTTOM)
		pBindInfo->m_BorderBindInfo[BOTTOM_BORDER].Target = NOT_BIND;
}

void CWndBinder::RepositionChildWindows()
{
	if(m_pParentWnd == NULL || m_pParentWnd->m_hWnd == NULL)
		return;

	const CRect& rect = m_rectParent;
	m_pParentWnd->GetClientRect(&m_rectParent);

	CWndBindMap::iterator it = m_WndBindMap.begin();
	for(;it != m_WndBindMap.end(); ++it)
	{
		CalcChildWndRectBindParent(&(it->second), rect);
		CalcChildWndRectBindParentBorder(&(it->second));
		CalcChildWndRectNotBind(&(it->second));
	}

	it = m_WndBindMap.begin();
	for(;it != m_WndBindMap.end(); ++it)
	{
		CalcChildWndRectBindChild(&(it->second));
		CalcChildWndRectNotBind(&(it->second));
	}

	it = m_WndBindMap.begin();
	for(;it != m_WndBindMap.end(); ++it)
	{
		WND_BIND_INFO& BindInfo = it->second;
		MoveWindow(BindInfo.hWnd, BindInfo.m_rectWnd.left, BindInfo.m_rectWnd.top, 
			BindInfo.m_rectWnd.Width(), BindInfo.m_rectWnd.Height(), TRUE);
	}
}

void CWndBinder::CalcChildWndRectBindParent(WND_BIND_INFO* pBindInfo, const CRect& rectClient)
{
	LONG* pBorderData;
	int nDestOrg;
	int nSrcOrg;
	int nDestExtent;
	int nSrcExtent;
	int nSrcBorderData;

	for(int i = 0; i < 4; ++i)
	{
		BORDER_BIND_INFO& BorderBind = pBindInfo->m_BorderBindInfo[i];

		if(BorderBind.Target != PARENT_WND)
			continue;

		switch(i)
		{
		case LEFT_BORDER:
			nDestOrg = rectClient.left;
			nSrcOrg = m_rectParentOrigin.left;
			nDestExtent = rectClient.Width();
			nSrcExtent = m_rectParentOrigin.Width();
			nSrcBorderData = pBindInfo->m_rectWndOrigin.left;
			pBorderData = &(pBindInfo->m_rectWnd.left);
			break;
		case TOP_BORDER:
			nDestOrg = rectClient.top;
			nSrcOrg = m_rectParentOrigin.top;
			nDestExtent = rectClient.Height();
			nSrcExtent = m_rectParentOrigin.Height();
			nSrcBorderData = pBindInfo->m_rectWndOrigin.top;
			pBorderData = &(pBindInfo->m_rectWnd.top);
			break;
		case RIGHT_BORDER:
			nDestOrg = rectClient.left;
			nSrcOrg = m_rectParentOrigin.left;
			nDestExtent = rectClient.Width();
			nSrcExtent = m_rectParentOrigin.Width();
			nSrcBorderData = pBindInfo->m_rectWndOrigin.right;
			pBorderData = &(pBindInfo->m_rectWnd.right);
			break;
		case BOTTOM_BORDER:
			nDestOrg = rectClient.top;
			nSrcOrg = m_rectParentOrigin.top;
			nDestExtent = rectClient.Height();
			nSrcExtent = m_rectParentOrigin.Height();
			nSrcBorderData = pBindInfo->m_rectWndOrigin.bottom;
			pBorderData = &(pBindInfo->m_rectWnd.bottom);
			break;
		}

		*pBorderData = CUtility::TransformCoordinate(nDestOrg, nDestExtent, nSrcBorderData, nSrcOrg,nSrcExtent);
	}
}

void CWndBinder::CalcChildWndRectBindParentBorder(WND_BIND_INFO* pBindInfo)
{
	LONG* pBorderData;

	for(int i = 0; i < 4; ++i)
	{
		BORDER_BIND_INFO& BorderBind = pBindInfo->m_BorderBindInfo[i];

		if(BorderBind.Target != PARENT_WND_BORDER)
			continue;

		switch(i)
		{
		case LEFT_BORDER:
			pBorderData = &(pBindInfo->m_rectWnd.left);
			break;
		case TOP_BORDER:
			pBorderData = &(pBindInfo->m_rectWnd.top);
			break;
		case RIGHT_BORDER:
			pBorderData = &(pBindInfo->m_rectWnd.right);
			break;
		case BOTTOM_BORDER:
			pBorderData = &(pBindInfo->m_rectWnd.bottom);
			break;
		}

		*pBorderData = *BorderBind.m_pBindData + BorderBind.m_nOffset;
	}

}

void CWndBinder::CalcChildWndRectBindChild(WND_BIND_INFO* pBindInfo)
{
	for(int i = 0; i < 4; ++i)
	{
		BORDER_BIND_INFO& BorderBind = pBindInfo->m_BorderBindInfo[i];

		if(BorderBind.Target != SIBLING_WND)
			continue;

		switch(i)
		{
		case LEFT_BORDER:
			pBindInfo->m_rectWnd.left = *(BorderBind.m_pBindData) + BorderBind.m_nOffset;
			break;
		case TOP_BORDER:
			pBindInfo->m_rectWnd.top = *(BorderBind.m_pBindData) + BorderBind.m_nOffset;
			break;
		case RIGHT_BORDER:
			pBindInfo->m_rectWnd.right = *(BorderBind.m_pBindData) + BorderBind.m_nOffset;
			break;
		case BOTTOM_BORDER:
			pBindInfo->m_rectWnd.bottom = *(BorderBind.m_pBindData) + BorderBind.m_nOffset;
			break;
		}

		
	}
}

void CWndBinder::CalcChildWndRectNotBind(WND_BIND_INFO* pBindInfo)
{
	int nWidth = pBindInfo->m_rectWndOrigin.Width();
	int nHeight = pBindInfo->m_rectWndOrigin.Height();

	for(int i = 0; i < 4; ++i)
	{
		BORDER_BIND_INFO& BorderBind = pBindInfo->m_BorderBindInfo[i];

		if(BorderBind.Target != NOT_BIND)
			continue;

		switch(i)
		{
		case LEFT_BORDER:
			pBindInfo->m_rectWnd.left = pBindInfo->m_rectWnd.right - nWidth;
			break;
		case TOP_BORDER:
			pBindInfo->m_rectWnd.top = pBindInfo->m_rectWnd.bottom - nHeight;
			break;
		case RIGHT_BORDER:
			pBindInfo->m_rectWnd.right = pBindInfo->m_rectWnd.left + nWidth;
			break;
		case BOTTOM_BORDER:
			pBindInfo->m_rectWnd.bottom = pBindInfo->m_rectWnd.top + nHeight;
			break;
		}
	}
}

void CWndBinder::BindChildToParent(int nFirstChildID, int nLastChildID, DWORD dwBindBorder)
{
	if(nLastChildID >= 0 && nLastChildID < nFirstChildID)
		return;

	HWND hChild = GetWindow(m_pParentWnd->m_hWnd, GW_CHILD);

	for(; hChild; hChild = GetWindow(hChild, GW_HWNDNEXT))
	{
		int nChildID = GetDlgCtrlID(hChild);

		if((nFirstChildID == 0 || nChildID >= nFirstChildID) && (nLastChildID < 0 || nChildID <= nLastChildID))
		{
			BindChildToParent(hChild, dwBindBorder);
		}
	}
}

void CWndBinder::UpdateOriginHeight(HWND hChild, int nHeight)
{
	WND_BIND_INFO* pBindInfo = FindBindInfo(hChild);

	if(pBindInfo)
	{
		pBindInfo->m_rectWndOrigin.bottom = pBindInfo->m_rectWndOrigin.top + nHeight;
	}
}
